<?php
namespace App\Repositories;

use App\Models\Topic;
use App\Models\Comment;
use App\Models\CommentDetail;
use App\Models\User;
use App\Events\CommentCreated;
use App\Repositories\AttachmentRepository;
use App\Models\AttachmentRelationship;
use Illuminate\Http\Request;
use App\Traits\ContentJson;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class CommentRepository
{
    use ContentJson;


    /**
     * 创建评论。当是主帖回复时更新楼层号，楼中楼不更新
     *
     * @param Request $request
     * @return number[]|string[]|array[]
     */
    public function create(Request $request)
    {
        $result = \DB::transaction(function () use ($request) {
            $rootId = $pid = 0;
            $parentComment = null;
            $rootComment = null;
            $user = auth_user();
            $log = __METHOD__ . ", user: {$user->id}";
            if ($request->pid) {
                //回复评论
                $parentComment = Comment::query()->withTrashed()->findOrFail($request->pid);
                $topic = $parentComment->topic;
                $log .= ", 回复评论：" . $parentComment->id;
                if ($parentComment->is_main_floor)
                {
                    $parentComment = null;//回复的是主楼，相当于新楼层，不能对主楼评论进行回复
                }
            } elseif ($request->tid) {
                //回复帖子
                $topic = Topic::query()->withTrashed()->find($request->tid);
                $log .= ", 回复帖子：" . $topic->id;

            } else {
                logic_abort('required tid or pid');
            }

            if ($parentComment)
            {
                $pid = $parentComment->id;

                if ($parentComment->root_id > 0)
                {
                    $rootId = $parentComment->root_id;
                    $rootComment = $parentComment->rootComment()->findOrFail($rootId);
                }
                else
                {
                    $rootId = $parentComment->id;
                    $rootComment = $parentComment;
                }
            }
            $log .= ", pid: $pid, rootId: $rootId";
            $contentArr = self::getContents($request->get('content_detail'));

            //创建评论
            $comment = $topic->comments()->create([
                'uid' => $user->id,
                'pid' => $pid,
                'root_id' => $rootId,
                'key' => \Uuid::uuid4(),
                'detail_excerpt' => self::getDetailExcerpt($contentArr['contents']),
            ]);
            $log .= ", new commentId: " . $comment->id;
            //创建详情
            $commentDetail = $comment->detail()->create([
                'content' => $contentArr['contents'],
            ]);
            //保存附件
            self::saveDetailImages($comment, $commentDetail, $contentArr['images']);

            //先插入，再更新楼层号
            if ($pid == 0)
            {
                $log .= ", pid == 0, 正常楼层";
                //非楼中楼，更新帖子的信息
                $count = $topic->directComments()->withTrashed()
                    ->where('id', '<=', $comment->id)
                    ->count();
                $floorNum = $count + 1;
                $log .= ", directComments count: $count, so floor_num = $floorNum, from sql: " . last_query();
                $comment->update(['floor_num' => $floorNum]);
                $topic->update([
                    'comment_count' => $count,
                    'last_comment_time' => time(),
                    'last_comment_id' => $comment->id,
                ]);
            }
            else
            {
                $log .= ", pid > 0, 楼中楼";
                //楼中楼，更新根评论的信息
                $count = $rootComment->children()->withTrashed()->count();
                $rootComment->update(['comment_count' => $count]);
                if ($count <= 5)
                {
                    $rootComment->firstComments()->save($comment);
                }
                //更新帖子信息
                $topic->update([
                    'last_comment_time' => time(),
                    'last_comment_id' => $comment->id,
                ]);
                //同样更新楼层，是楼中楼的楼层
                $count = $rootComment->children()->where('id', '<=', $comment->id)->withTrashed()->count();
                $log .= ", floor_num: $count, from sql: " . last_query();
                $comment->update(['floor_num' => $count]);

            }

            //插入评论成功，触发“评论添加事件”
            event(new CommentCreated($comment));

            logger($log);

            return normalize(0, "回复成功", [
                'topic' => $topic,
                'comment' => $comment,
                'parent_comment' => $parentComment,
                'root_comment' => $rootComment,
            ]);
        });

        return $result;

    }
    
    /**
     * 更新评论。评论本身没什么可更新，详情删除，重新添加新详情。
     * 
     * @param array $data
     * @param unknown $id
     * @return number[]|string[]|array[]
     */
    public function update(Request $request, $id)
    {
        $result = \DB::transaction(function () use ($request, $id) {
            $comment = Comment::query()->withTrashed()->findOrFail($id);

            $comment->detail->delete();
            if ($comment->hide) {
                $comment->hide->delete();
            }
            //创建详情
            $contentArr = self::getContents($request->get('content_detail'));
            $commentDetail = $comment->detail()->create([
                'content' => $contentArr['contents'],
            ]);
            //更新评论的摘要
            $comment->update(['detail_excerpt' => self::getDetailExcerpt($contentArr['contents'])]);
            //保存附件
            self::saveDetailImages($comment, $commentDetail, $contentArr['images']);

            //保存隐藏部分
            if ($request->get('has_hide')) {
                $contentArr = self::getContents($request->get('content_hide'));
                $commentHide = $comment->hide()->create([
                    'content' => $contentArr['contents'],
                    'require_reply' => $request->get('require_reply', 0),
                    'require_score' => $request->get('require_score', 0),
                ]);
                self::saveDetailImages($comment, $commentHide, $contentArr['images']);
            }

            return $comment;
        });

        return normalize(RET_OK, MSG_OK, $result);
    }
    
    /**
     * 列出一个话题下的评论，话题详情页，包含楼中楼数据
     * 
     * @param array $params
     * @return number[]|string[]|array[]
     */
    public function listOfTopic($id)
    {
        $topic = $this->topic->findOrFail($id);
        $comments = $this->comment
            ->where("tid", $topic->id)->where('pid', 0)
            ->with([
                'user', 'detail', 'firstComments',
                'firstComments.user', 'firstComments.detail',
                'firstComments.parentComment', 'firstComments.parentComment.user',
            ])
            ->orderBy('floor_num', 'asc')
            ->paginate(request('per_page', Comment::ADMIN_TOPIC_SHOW_PER_PAGE));
//        dd($comments);

        return ['topic' => $topic, 'list' => $comments];
    }
    
    /**
     * 取所有评论
     * 
     * @param array $params
     * @return number[]|string[]|array[]
     */
    public function listAll(Request $request, array $otherParams = [])
    {
        $defaults = [
            'orderBy' => 'id desc',
            'with' => ['user', 'detail'],
            'per_page' => 15,
        ];
        $params = array_merge($defaults, $otherParams);
        $comments = $this->comment
        ->when($request->tid, function ($query) use ($request) {return $query->where('tid', $request->tid);})
        ->when($request->root_id, function ($query) use ($request) {return $query->where('root_id', $request->root_id);})
        ->when($request->begin_time, function ($query) use ($request) {return $query->where('created_at', '>=', $request->begin_time);})
        ->when($request->end_time, function ($query) use ($request) {return $query->where('created_at', '<=', $request->end_time);})
        ->with($params['with'])
        ->orderByRaw($params['orderBy'])
        ->paginate($request->get('per_page', $params['per_page']));
    
        return $comments;
    }

    public static function getContents($content)
    {
        $contentOriginalArr = is_array($content) ? $content : json_decode($content, true);
        $contentArr = [];
        $imageArr = [];
        if ($contentOriginalArr && is_array($contentOriginalArr))
        {
            foreach ($contentOriginalArr as $item)
            {
                if ($item['type'] == CommentDetail::CONTENT_TYPE_TEXT)
                {
                    if (!empty($item['data']['text'])) {
                        $contentArr[] = [
                            'type' => CommentDetail::CONTENT_TYPE_TEXT,
                            'data' => [
                                'text' => $item['data']['text'],
                            ],
                        ];
                    }

                }
                elseif ($item['type'] == CommentDetail::CONTENT_TYPE_IMAGE)
                {
                    //图片不需要保存那么多字段，比如exif不要放在详情字段里边
                    $key = $item['data']['key'] ?? $item['data']['public_id'] ?? null;
                    if ($key) {
                        $contentArr[] = [
                            'type' => CommentDetail::CONTENT_TYPE_IMAGE,
                            'data' => [
                                'key' => $key,
                                'format' => $item['data']['imageInfo']['format'] ?? $item['data']['format'] ?? '',
                                //新上传的与旧的不同格式
                                'width' => $item['data']['imageInfo']['width'] ?? $item['data']['width'],
                                'height' => $item['data']['imageInfo']['height'] ?? $item['data']['height'],
                            ],
                        ];
                        $imageArr[] = $item['data'];//图片全部信息返回
                    }

                }
            }
        }
        else
        {
            $contentArr[] = [
                'type' => CommentDetail::CONTENT_TYPE_TEXT,
                'data' => ['text' => (string)$content],
            ];
        }
        return ['contents' => $contentArr, 'images' => $imageArr];
    }

    public static function getDateTimeFromExif(array $exif)
    {
        foreach (['DateTime', 'DateTimeDigitized', 'DateTimeOriginal'] as $field)
        {
            if (empty($exif[$field]))
            {
                continue;
            }
            $dateTime = $exif[$field]['val'];
            if (!preg_match('/[\s]+/', $dateTime))
            {
                $dateTime .= " 00:00:00";
            }
            return $dateTime;
        }
    }

    public static function getLatitudeFromExif(array $exif)
    {
        if (!empty($exif['GPSLatitude']))
        {
            $valueArr = preg_split('/[\s,]+/', $exif['GPSLatitude']['val']);
            return $valueArr[0] + ($valueArr[1]/60) + ($valueArr[2]/3600);
        }
    }

    public static function getLongitudeFromExif(array $exif)
    {
        if (!empty($exif['GPSLongitude']))
        {
            $valueArr = preg_split('/[\s,]+/', $exif['GPSLongitude']['val']);
            return $valueArr[0] + ($valueArr[1]/60) + ($valueArr[2]/3600);
        }
    }

    public static function saveDetailImages(Comment $comment, $commentDetailOrCommentHide, array $images)
    {
        foreach ($images as $image)
        {
            $commentDetailOrCommentHide->attachments()->create([
                'uid' => $comment->uid,
                'key' => $image['key'] ?? $image['public_id'],
                'mime_type' => "image/" . ($image['imageInfo']['format'] ?? $image['format']),
                'size' => $image['fsize'] ?? $image['bytes'],
                'width' => $image['imageInfo']['width'] ?? $image['width'],
                'height' => $image['imageInfo']['height'] ?? $image['height'],
                'shoot_time' => isset($image['exif']) ? self::getDateTimeFromExif($image['exif']) : null,
                'latitude' => isset($image['exif']) ? self::getLatitudeFromExif($image['exif']) : null,
                'longitude' => isset($image['exif']) ? self::getLongitudeFromExif($image['exif']): null,
            ]);
        }

        return true;
    }

    public static function getDetailExcerpt(array $contents)
    {
        $excerpt = '';
        foreach ($contents as $content) {
            if ($content['type'] == CommentDetail::CONTENT_TYPE_TEXT) {
                $excerpt .= $content['data']['text'];
                if (mb_strwidth($excerpt, 'utf-8') > Comment::DETAIL_EXCERPT_WIDTH) {
                    return Str::limit($excerpt, Comment::DETAIL_EXCERPT_WIDTH);
                }
            }
        }
        return $excerpt;
    }


    public function destroy($id)
    {
        $comment = Comment::query()->findOrFail($id);
        DB::transaction(function () use ($comment) {
            $comment->delete();
            if ($comment->is_main_floor) {
                $comment->topic->delete();
            }

        });

        return api(RET_OK, MSG_OK, []);

    }

    public function restore($id)
    {
        $comment = Comment::query()->withTrashed()->findOrFail($id);
        DB::transaction(function () use ($comment) {
            $comment->restore();
            if ($comment->is_main_floor) {
                $comment->topic->restore();
            }

        });

        return api(RET_OK, MSG_OK, []);

    }


    /**
     * 返回评论列表
     *
     * @param array $options 一些选项，可以是以下字段：
     *
     * with, 加载的关联
     * id, 指定 ID
     * root_id ，指定根 ID
     * tid, 指定帖子 ID
     * pid, 指定父 ID
     * uid，指定用户 ID
     * include_is_liked, 包含当前登录用户是否已点赞
     * per_page, 每页数量
     * page, 页码
     * order_by, 排序
     *
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function getList($options = [])
    {
        $query = Comment::query();

        //要取的关联
        if (!empty($options['with'])) {
            if (!is_array($options['with'])) {
                $options['with'] = explode(',', $options['with']);
            }
            $query->with($options['with']);
        }

        //回复后立即返回列表的结构，只需要新发的那条的信息。返回指定单条
        if (!empty($options['id'])) {
            $query->where('id', $options['id']);
        }

        if (!empty($options['root_id'])) {
            $query->where("root_id", $options['root_id']);
        }

        if (!empty($options['tid'])) {
            $query->where("tid", $options['tid']);
        }

        //注意，pid 有可能为 0
        if (isset($options['pid'])) {
            $query->where("pid", $options['pid']);
        }

        //指定用户回复的，不包含主楼
        if (!empty($options['uid'])) {
            $query->where("uid", $options['uid'])->where('is_main_floor', 0);
        }

        if (!empty($options['order_by'])) {
            foreach ($options['order_by'] as $column => $direction) {
                $query->orderBy($column, $direction);
            }
        } else {
            $query->orderBy('id', 'asc');
        }

        $comments = $query->paginate($options['per_page'] ?? 10, ['*'], 'page', $options['page'] ?? 1);

        if (!empty($options['include_is_liked'])) {
            $user = auth_user();
            if ($user) {
                $idArr = $comments->getCollection()->pluck('id')->toArray();
                $userLikedComments = $user->likeComments()->whereIn('comments.id', $idArr)->get(['comments.id'])->keyBy('id');
                foreach ($comments as &$item) {
                    $item->is_liked = $userLikedComments->has($item->id) ? 1 : 0;
                }
            }

        }

        return $comments;
    }

}